package com.ics.atable.chat.service.impl;

import com.google.gson.JsonObject;
import com.google.gson.Gson;
import com.ics.atable.chat.common.milvus.MilvusMultiVectorStore;
import com.ics.atable.chat.constant.VectorDatabaseConstant;
import com.ics.atable.chat.model.dto.AddressDTO;
import com.ics.atable.chat.model.dto.AddressEmbeddingDTO;
import com.ics.atable.chat.service.AddressService;
import com.ics.atable.chat.service.EmbeddingService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 地址Excel处理服务实现
 */
@Slf4j
@Service
public class AddressServiceImpl implements AddressService {

    private final EmbeddingService embeddingService;
    private final MilvusMultiVectorStore milvusMultiVectorStore;

    public AddressServiceImpl(EmbeddingService embeddingService,
                              MilvusMultiVectorStore milvusMultiVectorStore) {
        this.embeddingService = embeddingService;
        this.milvusMultiVectorStore = milvusMultiVectorStore;
    }

    @Override
    public List<AddressDTO> processExcelFile(MultipartFile file) throws Exception {
        log.info("开始处理地址Excel: {}, size={} bytes", file.getOriginalFilename(), file.getSize());

        validateExcelFile(file);

        List<AddressDTO> addresses = new ArrayList<>();

        Workbook workbook = null;
        try {
            workbook = new XSSFWorkbook(file.getInputStream());
            Sheet sheet = workbook.getSheetAt(0);

            // 表头校验
            Row headerRow = sheet.getRow(0);
            if (headerRow == null) {
                throw new IllegalArgumentException("Excel文件没有表头行");
            }
            validateHeaders(headerRow);

            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row == null) {
                    continue;
                }
                AddressDTO dto = parseRow(row);
                if (dto != null) {
                    addresses.add(dto);
                }
            }
        } catch (IOException e) {
            log.error("读取Excel失败", e);
            throw new Exception("读取Excel失败: " + e.getMessage());
        } finally {
            if (workbook != null) {
                try { workbook.close(); } catch (IOException ignore) {}
            }
        }

        log.info("地址Excel处理完成，共{}条", addresses.size());
        return addresses;
    }

    @Override
    public void validateExcelFile(MultipartFile file) throws IllegalArgumentException {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("文件不能为空");
        }
        String name = file.getOriginalFilename();
        if (name == null || !name.toLowerCase().endsWith(".xlsx")) {
            throw new IllegalArgumentException("只支持.xlsx格式的Excel文件");
        }
        if (file.getSize() > 10 * 1024 * 1024) {
            throw new IllegalArgumentException("文件大小不能超过10MB");
        }
    }

    @Override
    public List<AddressEmbeddingDTO> embedAndStore(List<AddressDTO> addresses) {
        List<AddressEmbeddingDTO> list = new ArrayList<>();

        if (addresses == null || addresses.isEmpty()) {
            return list;
        }

        // 仅向量化 addressName
        List<String> messages = new ArrayList<>();
        for (AddressDTO dto : addresses) {
            messages.add(dto.getAddressName());
        }

        // 输出向量化进度，并且计算时间单位秒
        long start = System.currentTimeMillis();
        log.info("开始向量化，共{}条", addresses.size());
        // 向量化 (使用分块处理避免超过2048限制)
        var response = embeddingService.embeddingByChunk(messages);
        log.info("向量化完成，耗时{}秒", (System.currentTimeMillis() - start) / 1000);

        // 解析结果并组装 AddressEmbeddingDTO（每个地址1个向量：addressName）
        int idx = 0;
        for (AddressDTO dto : addresses) {
            AddressEmbeddingDTO emb = new AddressEmbeddingDTO();
            emb.setOrgId(dto.getOrgId());
            emb.setAddressName(response.getResults().get(idx++).getOutput());
            emb.setMetadata(dto.getMetadata());
            emb.setProvinceName(dto.getProvinceName());
            emb.setCityName(dto.getCityName());
            emb.setCountyName(dto.getCountyName());
            emb.setTownName(dto.getTownName());
            emb.setVillageName(dto.getVillageName());
            list.add(emb);
        }

        // 写入向量数据库
        storeToMilvus(list);

        return list;
    }

    @Override
    public List<AddressEmbeddingDTO> processExcelAndStore(MultipartFile file) throws Exception {
        List<AddressDTO> addresses = processExcelFile(file);
        return embedAndStore(addresses);
    }

    private void storeToMilvus(List<AddressEmbeddingDTO> list) {
        try {
            if (milvusMultiVectorStore.hasCollection(VectorDatabaseConstant.ADDRESS_COLLECTION_NAME)){
                milvusMultiVectorStore.loadCollection(VectorDatabaseConstant.ADDRESS_COLLECTION_NAME);
                List<JsonObject> data = convertToMilvusDataFormat(list);
                
                // 分批插入数据，避免单次插入数据量过大导致gRPC消息超限
                int batchSize = 1024; // 每批插入1024条数据
                for (int i = 0; i < data.size(); i += batchSize) {
                    int endIndex = Math.min(i + batchSize, data.size());
                    List<JsonObject> batch = data.subList(i, endIndex);
                    milvusMultiVectorStore.insert(batch, VectorDatabaseConstant.ADDRESS_COLLECTION_NAME);
                }
            } else {
                log.info("集合不存在，开始创建集合: {}（未实现））", VectorDatabaseConstant.ADDRESS_COLLECTION_NAME);
//                // 构建仅包含 address_name 向量的集合
//                java.util.Map<String, java.util.List<String>> fieldMap = new java.util.HashMap<>();
//                fieldMap.put("id", java.util.List.of(VectorDatabaseConstant.AddressField.ORG_ID.getCode()));
//                fieldMap.put("dense", java.util.List.of(
//                        VectorDatabaseConstant.AddressField.ADDRESS_NAME.getCode()
//                ));
//                fieldMap.put("sparse", java.util.List.of());
//                fieldMap.put("metadata", java.util.List.of(VectorDatabaseConstant.AddressField.METADATA.getCode()));
//
//                boolean created = milvusMultiVectorStore.createCollection(VectorDatabaseConstant.ADDRESS_COLLECTION_NAME, fieldMap);
//                if (created) {
//                    milvusMultiVectorStore.loadCollection(VectorDatabaseConstant.ADDRESS_COLLECTION_NAME);
//                    List<JsonObject> data = convertToMilvusDataFormat(list);
//                    milvusMultiVectorStore.insert(data, VectorDatabaseConstant.ADDRESS_COLLECTION_NAME);
//                } else {
//                    log.error("创建集合失败: {}", VectorDatabaseConstant.ADDRESS_COLLECTION_NAME);
//                }
            }
        } catch (Exception e) {
            log.error("导入地址向量数据失败: {}", VectorDatabaseConstant.ADDRESS_COLLECTION_NAME, e);
        } finally {
            milvusMultiVectorStore.releaseCollection(VectorDatabaseConstant.ADDRESS_COLLECTION_NAME);
        }
    }

    private List<JsonObject> convertToMilvusDataFormat(List<AddressEmbeddingDTO> list) {
        Gson gson = new Gson();
        List<JsonObject> milvusDataList = new ArrayList<>();
        for (AddressEmbeddingDTO item : list) {
            JsonObject json = new JsonObject();
            json.addProperty(VectorDatabaseConstant.AddressField.ORG_ID.getCode(), item.getOrgId());
            json.add(VectorDatabaseConstant.AddressField.ADDRESS_NAME.getCode(), gson.toJsonTree(item.getAddressName()));
            if (item.getMetadata() != null) {
                json.addProperty(VectorDatabaseConstant.AddressField.METADATA.getCode(), item.getMetadata());
            }
            json.addProperty(VectorDatabaseConstant.AddressField.PROVINCE_NAME.getCode(), item.getProvinceName());
            json.addProperty(VectorDatabaseConstant.AddressField.CITY_NAME.getCode(), item.getCityName());
            json.addProperty(VectorDatabaseConstant.AddressField.COUNTY_NAME.getCode(), item.getCountyName());
            json.addProperty(VectorDatabaseConstant.AddressField.TOWN_NAME.getCode(), item.getTownName());
            json.addProperty(VectorDatabaseConstant.AddressField.VILLAGE_NAME.getCode(), item.getVillageName());
            milvusDataList.add(json);
        }
        return milvusDataList;
    }

    private float[] toFloatArray(List<Double> doubles) {
        float[] arr = new float[doubles.size()];
        for (int i = 0; i < doubles.size(); i++) {
            arr[i] = doubles.get(i).floatValue();
        }
        return arr;
    }
    private void validateHeaders(Row headerRow) {
        String[] expected = {"org_id", "village_name", "town_name", "county_name", "city_name", "province_name"};
        for (int i = 0; i < expected.length; i++) {
            Cell cell = headerRow.getCell(i);
            String actual = cell != null ? getCellValueAsString(cell) : "";
            if (!expected[i].equals(actual)) {
                throw new IllegalArgumentException(
                        String.format("表头不匹配，第%d列期望'%s'，实际'%s'", i + 1, expected[i], actual));
            }
        }
    }

    private AddressDTO parseRow(Row row) {
        try {
            AddressDTO dto = new AddressDTO();
            dto.setOrgId(getCellValueAsString(row.getCell(0)));
            dto.setVillageName(getCellValueAsString(row.getCell(1)));
            dto.setTownName(getCellValueAsString(row.getCell(2)));
            dto.setCountyName(getCellValueAsString(row.getCell(3)));
            dto.setCityName(getCellValueAsString(row.getCell(4)));
            dto.setProvinceName(getCellValueAsString(row.getCell(5)));
            // 构造 addressName：按 省-市-县-镇-村 拼接，忽略空值
            String addressName = joinNonEmpty(
                    dto.getProvinceName(),
                    dto.getCityName(),
                    dto.getCountyName(),
                    dto.getTownName(),
                    dto.getVillageName()
            );
            dto.setAddressName(addressName);

            // 构造 metadata JSON
            JsonObject meta = new JsonObject();
            meta.addProperty("org_id", nullToEmpty(dto.getOrgId()));
            meta.addProperty("province_name", nullToEmpty(dto.getProvinceName()));
            meta.addProperty("city_name", nullToEmpty(dto.getCityName()));
            meta.addProperty("town_name", nullToEmpty(dto.getTownName()));
            meta.addProperty("county_name", nullToEmpty(dto.getCountyName()));
            meta.addProperty("village_name", nullToEmpty(dto.getVillageName()));
            meta.addProperty("address_name", nullToEmpty(addressName));
            dto.setMetadata(meta.toString());

            if (isEmpty(dto.getOrgId()) && isEmpty(dto.getVillageName()) && isEmpty(dto.getTownName())
                    && isEmpty(dto.getCountyName()) && isEmpty(dto.getCityName()) && isEmpty(dto.getProvinceName())) {
                return null;
            }
            return dto;
        } catch (Exception e) {
            log.warn("解析第{}行失败: {}", row.getRowNum() + 1, e.getMessage());
            return null;
        }
    }

    private boolean isEmpty(String s) { return s == null || s.trim().isEmpty(); }

    private String nullToEmpty(String s) { return s == null ? "" : s; }

    private String joinNonEmpty(String... parts) {
        StringBuilder sb = new StringBuilder();
        for (String part : parts) {
            if (!isEmpty(part)) {
                if (!sb.isEmpty()) { sb.append(""); }
                sb.append(part.trim());
            }
        }
        return sb.toString();
    }

    private String getCellValueAsString(Cell cell) {
        if (cell == null) { return ""; }
        switch (cell.getCellType()) {
            case STRING: return cell.getStringCellValue().trim();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) { return cell.getDateCellValue().toString(); }
                double v = cell.getNumericCellValue();
                if (v == (long) v) { return String.valueOf((long) v); }
                return String.valueOf(v);
            case BOOLEAN: return String.valueOf(cell.getBooleanCellValue());
            case FORMULA: return cell.getCellFormula();
            default: return "";
        }
    }
}


